#include "modbusrtumaster.h"
//#include "version.h"
#include "common.h"
#include <QHeaderView>


ModbusRtuMaster::ModbusRtuMaster(QString port,QString baud,QString dataBits,QString parity,QString stopBits,QWidget *parent) :
    QWidget(parent),
    m_modelTxPktQueue(new QStandardItemModel(this) ),
    m_modelTxRxResult(new QStandardItemModel(this) ),
    m_threadForSerial(new QThread(0) ),
    m_timerAutoSend(new QTimer(this))
{
    onBtnRefreshClicked();
    m_timerAutoSend->setSingleShot(true);
    mComPortSetting.cmbComPort = port;
    mComPortSetting.cmbBaudrate = baud;
    mComPortSetting.cmbDataBits = dataBits;
    mComPortSetting.cmbParity = parity;
    mComPortSetting.cmbStopBits = stopBits;
    QStringList headers;
    headers << "ENABLE" << "HEX" << "ASCII" << "COMMENT";
    m_modelTxPktQueue->setHorizontalHeaderLabels(headers);
    m_viewTxQueueData =  new QTableView(this);
    m_viewTxQueueData->setModel(m_modelTxPktQueue);
    m_viewTxQueueData->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
    m_viewTxQueueData->setSelectionMode(QAbstractItemView::QAbstractItemView::SingleSelection);
    m_viewTxQueueData->setSelectionBehavior(QAbstractItemView::SelectRows);
    headers.clear();
    headers << "TIME STAMP" << "STATUS" << "MODE" << "RAW DATA" <<  "CRC" << "S ADDR" << "FUNC" << "INFO";
    m_modelTxRxResult->setHorizontalHeaderLabels(headers);
    m_viewTxRxResult = new QTableView(this);
    m_viewTxRxResult->setModel(m_modelTxRxResult);
    m_viewTxRxResult->setEditTriggers(QAbstractItemView::NoEditTriggers);
    m_viewTxRxResult->horizontalHeader()->setStretchLastSection(true);
    m_viewTxRxResult->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
    m_viewTxRxResult->setSelectionMode(QAbstractItemView::QAbstractItemView::SingleSelection);
    m_viewTxRxResult->setSelectionBehavior(QAbstractItemView::SelectRows);
    m_rxCount = 0;
    m_txCount = 0;
    m_errorCount = 0;
    m_txRxResultMaxRow = 20000;
    createConnection();
    readSettings();
}

ModbusRtuMaster::~ModbusRtuMaster()
{
    m_threadForSerial->terminate();
    m_threadForSerial->exit();
    if(m_port)
    {
        delete m_port;
    }
    if(m_viewTxQueueData)
        delete m_viewTxQueueData;
    if(m_viewTxRxResult)
        delete m_viewTxRxResult;
}

ModbusRtuMaster *ModbusRtuMaster::m_modbusWndInstance = NULL;
ModbusRtuMaster *ModbusRtuMaster::getobj(QString port,QString baud,QString dataBits,QString parity,QString stopBits)
{
    if(m_modbusWndInstance == NULL)
        m_modbusWndInstance = new ModbusRtuMaster( port, baud, dataBits, parity, stopBits);
    return m_modbusWndInstance;
}

void ModbusRtuMaster::createConnection()
{
    m_port = new SerialPort(0);
    // for serial
    connect(m_port,                     SIGNAL(sgReceivedData(QByteArray)),     this,               SLOT(onRecvedData(QByteArray) ));
    connect(m_port,                     SIGNAL(sgReceivedErrorData(QByteArray)),this,               SLOT(onRecvedErrorData(QByteArray) ));
    connect(m_port,                     SIGNAL(sgSendedData(QByteArray)),       this,               SLOT(onSendedData(QByteArray)   ));
    connect(m_threadForSerial,          SIGNAL(finished()),                     m_port,             SLOT(deleteLater())                 );
    connect(m_threadForSerial,          SIGNAL(started()),                      m_port,             SLOT(init())                 );
    connect(m_port,                     SIGNAL(sgConnected()),                  this,               SLOT(onPortConnected()                        ));
    connect(m_port,                     SIGNAL(sgDisconnected()),               this,               SLOT(onPortDisconnected()                     ));
    connect(m_port,                     SIGNAL(sgResponseTimeout()),            this,               SLOT(onPortResponseTimeout()                  ));
    connect(m_port,                     SIGNAL(sgT15IntervalChanged()),         this,               SLOT(onT15IntervalChanged()                   ));
    connect(m_port,                     SIGNAL(sgT35IntervalChanged()),         this,               SLOT(onT35IntervalChanged()                   ));

    //时机很重要，所以要提高优先顺序
    m_port->moveToThread(m_threadForSerial);
    m_threadForSerial->start(QThread::TimeCriticalPriority);

    onLineModbusEditingFinished();//设置串口超时时间
    connect(this,                       SIGNAL(toggleAutoSend(bool)),           this,               SLOT(onChkAutoSendToggled(bool)               ));
    connect(this,                       SIGNAL(sgRxCountChanged()),             this,               SLOT(onRxCountChanged()                       ));
    connect(this,                       SIGNAL(sgTxCountChanged()),             this,               SLOT(onTxCountChanged()                       ));
    connect(this,                       SIGNAL(sgErrorCountChanged()),          this,               SLOT(onErrorCountChanged()                       ));
    connect(this,                       SIGNAL(sendWriteList(QStringList)),     this,               SLOT(onReceiveWriteList(QStringList) ),Qt::DirectConnection);
    connect(m_modelTxPktQueue,          SIGNAL(dataChanged(QModelIndex,QModelIndex)),
            this, SLOT(onModelTxPktQueueDataChanged(QModelIndex, QModelIndex) ));
    connect(m_timerAutoSend,            SIGNAL(timeout()),                      this,               SLOT(onTimerAutoSendTimeout()                 ));
    //connect(m_modelTxRxResult,          SIGNAL(rowsInserted(const QModelIndex &, int, int)),
    //        this, SLOT(onModelTxResultRowInserted(const QModelIndex &, int, int)));  //20181214 james remark
}

void ModbusRtuMaster::readSettings()
{
    QSettings settings(APP_CONFIG_FILE, QSettings::IniFormat);
    QString cmbComport = settings.value("cmbComport").toString();
    QString cmbBaudrate = settings.value("cmbBaudrate").toString();
    QString cmbDataBits = settings.value("cmdDataBits").toString();
    QString cmbParity  = settings.value("cmbParity").toString();
    QString cmbStopBits = settings.value("cmbStopBits").toString();
    QString lineAddr = settings.value("lineAddr").toString();
    QString lineInterval = settings.value("lineInterval").toString();
    QString lineSlaveAddr = settings.value("lineSlaveAddr").toString();
    QString lineWriteData = settings.value("lineWriteData").toString();
    QString cmbFunction = settings.value("cmbFunction").toString();
    QString cmbSendCount = settings.value("cmbSendCount").toString();
    bool chkAutoSend = settings.value("chkAutoSend").toBool();
    QString lineModbusTimeout = settings.value("lineModbusTimeout").toString();
    QString cmbCommMode = settings.value("cmbCommMode").toString();
    QStringList txQueueData = settings.value("txQueueData").toStringList();
    foreach(QString str, txQueueData)
    {
        QList<QStandardItem*> items;
        QStringList tempStrs = str.split(",");

        for(int i = 0; i < MODBUS_WND_TX_QUEUE_COLUMNS::COUNT; i++ )
        {
            QStandardItem* insertedItem = new QStandardItem();

            switch(i)
            {
            case MODBUS_WND_TX_QUEUE_COLUMNS::ENABLE:
                insertedItem->setCheckable(true);
                insertedItem->setEditable(false);
                break;
            case MODBUS_WND_TX_QUEUE_COLUMNS::ASCII:
                insertedItem->setEditable(false);
                insertedItem->setEditable(false);
                insertedItem->setText(tempStrs.at(i-1).toLatin1());
            default:
                insertedItem->setText(tempStrs.at(i-1));
            }
            items << insertedItem;
        }
        m_modelTxPktQueue->appendRow(items);
    }
    m_port->setMode("RTU");
    qDebug() << Q_FUNC_INFO << cmbComport << cmbBaudrate << cmbDataBits << cmbParity << cmbStopBits <<
                lineAddr << lineInterval << lineSlaveAddr << lineWriteData << cmbFunction << cmbSendCount << chkAutoSend << lineModbusTimeout;
}

void ModbusRtuMaster::closeEvent(QCloseEvent * event)
{
    Q_UNUSED(event);
    QSettings settings(APP_CONFIG_FILE, QSettings::IniFormat);
    QStringList txQueueData;
    for( int i = 0; i < m_modelTxPktQueue->rowCount(); i ++ )
    {
        txQueueData << m_modelTxPktQueue->item(i, MODBUS_WND_TX_QUEUE_COLUMNS::HEX)->text() + " ," +
                       m_modelTxPktQueue->item(i, MODBUS_WND_TX_QUEUE_COLUMNS::ASCII)->text() + " ," +
                       m_modelTxPktQueue->item(i, MODBUS_WND_TX_QUEUE_COLUMNS::COMMENT)->text();
    }
}

void ModbusRtuMaster::addDataToTxRxResultModel(QStringList info)
{
    QStandardItemModel* model = m_modelTxRxResult;
    QList <QStandardItem*> addItems;
    QBrush foregroundBursh(Qt::black);
    for ( int i = 0 ; i < MODBUS_WND_TXRX_RESULT_COLUMNS::COUNT ; i ++ )
    {
        QStandardItem* tempItem = new QStandardItem();
        switch(i)
        {
        case MODBUS_WND_TXRX_RESULT_COLUMNS::TIMESTAMP:
        {
            QString timeStamp = info.at(MODBUS_WND_TXRX_RESULT_COLUMNS::TIMESTAMP);
            QString status = info.at(MODBUS_WND_TXRX_RESULT_COLUMNS::STATUS);
            if( status.contains("RX") == true )
                foregroundBursh.setColor(Qt::blue);
            tempItem->setText(timeStamp);
            break;
        }
        case MODBUS_WND_TXRX_RESULT_COLUMNS::STATUS           :
        {
            QString status = info.at(MODBUS_WND_TXRX_RESULT_COLUMNS::STATUS);
            if( status.contains("NOK") == true )
                foregroundBursh.setColor(Qt::red);
            tempItem->setText(status);
            break;
        }
        case MODBUS_WND_TXRX_RESULT_COLUMNS::MODE           :
        {
            QString status = info.at(MODBUS_WND_TXRX_RESULT_COLUMNS::MODE);
            tempItem->setText(status);
            break;
        }
        case MODBUS_WND_TXRX_RESULT_COLUMNS::RAWDATA        :
            tempItem->setText(info.at(MODBUS_WND_TXRX_RESULT_COLUMNS::RAWDATA));
            break;
        case MODBUS_WND_TXRX_RESULT_COLUMNS::CRC        :
            tempItem->setText(info.at(MODBUS_WND_TXRX_RESULT_COLUMNS::CRC));
            break;

        case MODBUS_WND_TXRX_RESULT_COLUMNS::SLAVE_ADDR             :
            tempItem->setText("[" +info.at(MODBUS_WND_TXRX_RESULT_COLUMNS::SLAVE_ADDR) + "]");
            break;
        case MODBUS_WND_TXRX_RESULT_COLUMNS::FUNCTION        :
            tempItem->setText("[" + info.at(MODBUS_WND_TXRX_RESULT_COLUMNS::FUNCTION) + "]");
            break;
        case MODBUS_WND_TXRX_RESULT_COLUMNS::INFO        :
            tempItem->setText(info.at(MODBUS_WND_TXRX_RESULT_COLUMNS::INFO));
            break;
        }
        addItems << tempItem;
    }
    foreach(QStandardItem* item, addItems)
    {
        item->setEditable(false);
        item->setForeground(foregroundBursh);
    }
    model->appendRow(addItems);
}

void ModbusRtuMaster::addDataToTxPktQueueModel(QStringList parsedData)
{
    QStandardItemModel* model = m_modelTxPktQueue;

    QList <QStandardItem*> addItems;
    QBrush foregroundBursh(Qt::black);
    for ( int i = 0 ; i < MODBUS_WND_TX_QUEUE_COLUMNS::COUNT ; i ++ )
    {
        QStandardItem* tempItem = new QStandardItem();
        switch(i)
        {
        case MODBUS_WND_TX_QUEUE_COLUMNS::ENABLE:
        {
            tempItem->setCheckable(true);
            tempItem->setCheckState(Qt::Checked);
            tempItem->setEditable(false);
            break;
        }
        case MODBUS_WND_TX_QUEUE_COLUMNS::HEX        :
            tempItem->setEditable(true);
            tempItem->setText(parsedData.at(MODBUS_WND_TXRX_RESULT_COLUMNS::RAWDATA));
            break;

        case MODBUS_WND_TX_QUEUE_COLUMNS::ASCII        :
        {
            tempItem->setEditable(false);

            QByteArray text;
            QByteArray targetText = "";
            text = parsedData.at(MODBUS_WND_TXRX_RESULT_COLUMNS::RAWDATA).toLatin1().trimmed();
            text = text.replace(" " , "");

            for (int i =0; i < text.count() ; i = i+2 )
            {
                targetText +=QByteArray::fromHex(text.mid(i, 2));

            }
            tempItem->setText(targetText);
        }
            break;

        case MODBUS_WND_TX_QUEUE_COLUMNS::COMMENT        :
            tempItem->setCheckable(false);
            tempItem->setEditable(true);
            tempItem->setText("\"comment\"");
            break;
        }
        addItems << tempItem;
    }
    foreach(QStandardItem* item, addItems)
    {
        item->setForeground(foregroundBursh);
    }
    model->appendRow(addItems);
}

void ModbusRtuMaster::onSendedData(QByteArray data)
{
    QStringList sendedData;
    onParseData(sendedData, data);
    sendedData[MODBUS_WND_TXRX_RESULT_COLUMNS::STATUS] = "[TX " + sendedData.at(MODBUS_WND_TXRX_RESULT_COLUMNS::STATUS);
    addDataToTxRxResultModel(sendedData);
    qDebug() << Q_FUNC_INFO << sendedData;
    addTxCount();
}

void ModbusRtuMaster::onRecvedData(QByteArray data)
{
    m_mutexFotSerial.lock();
    QStringList receivedData;
    onParseData(receivedData, data);
    receivedData[MODBUS_WND_TXRX_RESULT_COLUMNS::STATUS] = "[RX " + receivedData.at(MODBUS_WND_TXRX_RESULT_COLUMNS::STATUS);
    addDataToTxRxResultModel(receivedData);
    if(receivedData.at(MODBUS_WND_TXRX_RESULT_COLUMNS::FUNCTION) == "02")
    {
        QString warnstring = receivedData.at(MODBUS_WND_TXRX_RESULT_COLUMNS::RAWDATA);
        QByteArray warnarr = QByteArray::fromHex(warnstring.toLatin1());
        int temp = (warnarr.at(3)&0xFF)<<8 | (warnarr.at(4)&0XFF);
        sendWarnData_fromModbus(temp);//警报值
    }
    addRxCount();
    QStringList list;
    list.clear();
    QString rawString;
    rawString.clear();
    for(int i = 0; i<receivedData.count();i++)
    {
        if(i == 3)
        {
            rawString = receivedData.at(i);
            list = rawString.split(" ");
            if(list.count() == 35) //TODO  this can be optimize , not based the data length
            {
                emit sendStandardData_fromModbus(receivedData.at(i));
            }
        }
        if( i == 6)
        {
            if(receivedData.at(i)=="05" || receivedData.at(i)=="10")
            {
                emit sendStandardData_fromModbus("");
            }
        }
    }
    qDebug() << Q_FUNC_INFO <<receivedData;
    m_mutexFotSerial.unlock();
}


void ModbusRtuMaster::onRecvedErrorData(QByteArray data)
{
    m_mutexFotSerial.lock();
    QStringList receivedData;
    onParseData(receivedData, data);
    receivedData[MODBUS_WND_TXRX_RESULT_COLUMNS::STATUS] = "[RX " + receivedData.at(MODBUS_WND_TXRX_RESULT_COLUMNS::STATUS);
    addDataToTxRxResultModel(receivedData);
    addRxCount();
    qDebug() << Q_FUNC_INFO <<receivedData;
    m_mutexFotSerial.unlock();
}

int ModbusRtuMaster::onParseData(QStringList &parsedData, QByteArray data)
{
    QString timeStamp = QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
    QString status = "";
    QString rawData = QString(ModbusRtuMaster::toHexString(data));
    QString slaveAddr = "";
    QString function = "";
    QString info = "";
    QString crc = "";
    QString commMode = "RTU";

    int exception_code = 0x00;
    if( commMode == "RTU")
    {
        if( data.count() >= 4 )
        {
            slaveAddr = QString( data.mid(0, 1).toHex() );
            function = QString( data.mid(1, 1).toHex() );
            crc = QString( data.mid(data.count() -2 , 2).toHex() );

            if( data.at(1) & 0x80 )
                exception_code = data.at(2);

            quint16 crc_result = 0x0ffff;
            if( check_ModbusRTU_CRC16(data, crc_result) == true )
            {
                if( exception_code != 0x00 )
                {
                    info += QString("[ERROR CODE] ") + data.mid(2, data.count() - 4).toHex();
                    exception_code = 0xff;
                }
            }
            else
            {
                info = QString("[ERROR CRC] expected %1").arg(crc_result, 4, 16, QChar('0'));
                exception_code = 0xff;
            }
        }
        else
        {
            exception_code = 0xff;
        }
    }
    else if( commMode == "LSBUS")
    {
        if( data.count() >= 4 )
        {
            QString response_start = QString( data.mid(0, 1).toHex() );
            slaveAddr = QString( data.mid(1, 2) );
            function = QString( data.mid(3, 1) );
            crc = QString( data.mid(data.count() -3, 2) );

            quint8 sum = LSBUS_sum(data.mid(0, data.count() - 3));

            if( sum == crc.toInt(NULL, 16) )
            {
                if( response_start == "15" )
                {
                    info += QString("[ERROR CODE] ") + data.mid(4, 2) + "  ";
                    exception_code = 0xff;
                }
                info += "" + data.mid(0, data.count()) ;
            }
            else
            {
                info = QString("[ERROR SUM] expected %1").arg(sum, 4, 16, QChar('0'));
                exception_code = 0xff;
            }
        }
        else
        {
            exception_code = 0xff;
        }
    }

    if( exception_code != 0x00 )
    {
        status = "NOK]";
    }
    else
    {
        status = "OK]";
    }

    parsedData << timeStamp << status << commMode << rawData << crc << slaveAddr << function << info;
    qDebug() << parsedData;
    qDebug() << "OK]OK]OK]OK]OK]OK]OK]v";
    return exception_code;
}

int ModbusRtuMaster::onParseData(QStringList &parsedData, QByteArray data, QByteArray minfo)
{
    QString timeStamp = QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
    QString status = "";
    QString rawData = QString(ModbusRtuMaster::toHexString(data));
    QString slaveAddr = "";
    QString function = "";
    QString info = QString(minfo);
    QString crc = "";
    QString commMode = "RTU";

    int exception_code = 0x00;
    if( commMode == "RTU")
    {
        if( data.count() >= 4 )
        {
            slaveAddr = QString( data.mid(0, 1).toHex() );
            function = QString( data.mid(1, 1).toHex() );
            crc = QString( data.mid(data.count() -2 , 2).toHex() );

            if( data.at(1) & 0x80 )
                exception_code = data.at(2);

            quint16 crc_result = 0x0ffff;
            if( check_ModbusRTU_CRC16(data, crc_result) == true )
            {
                if( exception_code != 0x00 )
                {
                    info += QString("[ERROR CODE] ") + data.mid(2, data.count() - 4).toHex();
                    exception_code = 0xff;
                }
            }
            else
            {
                info = QString("[ERROR CRC] expected %1").arg(crc_result, 4, 16, QChar('0'));
                exception_code = 0xff;
            }
        }
        else
        {
            exception_code = 0xff;
        }
    }
    else if( commMode == "LSBUS")
    {
        if( data.count() >= 4 )
        {
            QString response_start = QString( data.mid(0, 1).toHex() );
            slaveAddr = QString( data.mid(1, 2) );
            function = QString( data.mid(3, 1) );
            crc = QString( data.mid(data.count() -3, 2) );

            quint8 sum = LSBUS_sum(data.mid(0, data.count() - 3));

            if( sum == crc.toInt(NULL, 16) )
            {
                if( response_start == "15" )
                {
                    info += QString("[ERROR CODE] ") + data.mid(4, 2) + "  ";
                    exception_code = 0xff;
                }
                info += "" + data.mid(0, data.count()) ;
            }
            else
            {
                info = QString("[ERROR SUM] expected %1").arg(sum, 4, 16, QChar('0'));
                exception_code = 0xff;
            }
        }
        else
        {
            exception_code = 0xff;
        }
    }

    if( exception_code != 0x00 )
    {
        status = "NOK]";
    }
    else
    {
        status = "OK]";
    }

    parsedData << timeStamp << status << commMode << rawData << crc << slaveAddr << function << info;
    return exception_code;
}

quint32 ModbusRtuMaster::onCmbBaudrateTextChanged(QString str)
{
    QMetaObject::invokeMethod(m_port, "changeBaudrate", Qt::BlockingQueuedConnection,
                              Q_ARG(quint32, str.toInt()  ));

    return str.toInt();
}

quint32 ModbusRtuMaster::onCmbDataBitsTextChanged(QString str)
{
    QMetaObject::invokeMethod(m_port, "changeDataBits", Qt::BlockingQueuedConnection,
                              Q_ARG(quint32, str.toInt()  ));

    return str.toInt();
}
quint32 ModbusRtuMaster::onCmbParityTextChanged(QString str)
{
    QSerialPort::Parity parity = QSerialPort::UnknownParity;

    if( str == "none" )
    {
        parity = QSerialPort::NoParity;
    }
    else if ( str == "even" )
    {
        parity = QSerialPort::EvenParity;
    }
    else if ( str == "odd" )
    {
        parity = QSerialPort::OddParity;
    }

    QMetaObject::invokeMethod(m_port, "changeParity", Qt::BlockingQueuedConnection,
                              Q_ARG(quint32, parity  ));

    return parity;
}
quint32 ModbusRtuMaster::onCmbStopBitsTextChanged(QString str)
{
    QSerialPort::StopBits stopBits = QSerialPort::UnknownStopBits;
    if( str.toInt() == 1 )
        stopBits = QSerialPort::OneStop;
    else if ( str.toInt() == 2)
        stopBits = QSerialPort::TwoStop;

    QMetaObject::invokeMethod(m_port, "changeStopBits", Qt::BlockingQueuedConnection,
                              Q_ARG(quint32, stopBits  ));

    return stopBits;
}

void ModbusRtuMaster::onChkAutoSendToggled(bool checked)
{
    int interval = 300;
    if( checked == true )
    {
        if( interval == 0 )
        {
            interval = 1;
        }
        connect(m_port, SIGNAL(sgReadyEntered()), m_timerAutoSend, SLOT(start())  );
        m_timerAutoSend->setInterval( interval );
        m_timerAutoSend->start();
    }
    else
    {
        disconnect(m_port, SIGNAL(sgReadyEntered()), 0, 0);
        m_timerAutoSend->stop();
    }
}

void ModbusRtuMaster::onChkIgnoreAckToggled(bool checked)
{
    if( checked == true )
    {
        m_port->setIgnoreAck(true);
    }
    else
    {
        m_port->setIgnoreAck(false);
    }
}

void ModbusRtuMaster::onLineModbusEditingFinished()
{
    m_port->setResponseTimeout(500 );//设置Port超时时间
}

void ModbusRtuMaster::onPortConnected()
{
    emit sgConnected("Connected");
}
void ModbusRtuMaster::onPortDisconnected()
{
    m_timerAutoSend->stop();
    emit sgDisconnected( m_port->errorString());
    onTryConnectedPort();
}
void ModbusRtuMaster::onPortResponseTimeout()
{

#if 0
    namespace MODBUS_WND_TXRX_RESULT_COLUMNS
    {
    enum {
        TIMESTAMP           ,
        STATUS              ,
        MODE                ,
        RAWDATA             ,
        CRC                 ,
        SLAVE_ADDR          ,
        FUNCTION            ,
        INFO				,
        COUNT
    };
    }
#endif


    QString timeStamp = QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
    QString status = "[RX NOK]";
    QString rawData = "";
    QString slaveAddr = "";
    QString function = "";
    QString info = QString("[ERROR TIMEOUT INTERVAL] %1 msec").arg(QString("%1").arg(m_port->responseTimeout()));
    QString crc = "";
    QStringList rowItems;
    QString commMode = "";
    rowItems << timeStamp << status << commMode << rawData << crc << slaveAddr << function << info ;
    addDataToTxRxResultModel(rowItems);
    addErrorCount();
}

void ModbusRtuMaster::onTryConnectedPort()
{
    QMetaObject::invokeMethod(m_port, "tryConnect", Qt::QueuedConnection ,
                              Q_ARG(QString, mComPortSetting.cmbComPort),
                              Q_ARG(quint32, onCmbBaudrateTextChanged(mComPortSetting.cmbBaudrate) ),
                              Q_ARG(quint32, onCmbDataBitsTextChanged(mComPortSetting.cmbDataBits) ),
                              Q_ARG(quint32, onCmbParityTextChanged(mComPortSetting.cmbParity) ),
                              Q_ARG(quint32, onCmbStopBitsTextChanged(mComPortSetting.cmbStopBits) )
                              );

}

void ModbusRtuMaster::onBtnCloseClicked()
{
    QMetaObject::invokeMethod(m_port, "tryDisconnect", Qt::QueuedConnection);
}

void ModbusRtuMaster::onBtnManualSendClicked()
{
    sendModelTxPktQueue(-1);
}

void ModbusRtuMaster::onBtnScreenClearClicked()
{
    resetRxCount();
    resetTxCount();
    resetErrorCount();
    removeModelTxRxResult();
}
void ModbusRtuMaster::removeModelTxRxResult()
{
    int removeCount = m_modelTxRxResult->rowCount();
    for ( int i = 0; i < removeCount; i++ )
    {
        m_modelTxRxResult->removeRow(0);
    }
}

void ModbusRtuMaster::onBtnRefreshClicked()
{
    //    QList<QSerialPortInfo> portInfos = QSerialPortInfo::availablePorts();
    //    ui->cmbComPort->clear();
    //    foreach( QSerialPortInfo portInfo, portInfos)
    //     {
    //        QString portName = portInfo.portName();
    //        if( portInfo.isBusy() )
    //            ui->cmbComPort->addItem(portName + "(in use)");
    //        else
    //            ui->cmbComPort->addItem(portName);
    //    }
}



void ModbusRtuMaster::onRxCountChanged()
{
    //    qDebug()<< Q_FUNC_INFO << "m_rxCount:"<<m_rxCount;
}

void ModbusRtuMaster::onTxCountChanged()
{
    //    qDebug()<< Q_FUNC_INFO << "m_txCount:"<<m_txCount;
}
void ModbusRtuMaster::onErrorCountChanged()
{
    qDebug()<< Q_FUNC_INFO << "error Count:"<<m_errorCount;
}

void ModbusRtuMaster::onT15IntervalChanged()
{
    //    qDebug()<< Q_FUNC_INFO << "m_port->t15IntervalUsec() "<<m_port->t15IntervalUsec();
}

void ModbusRtuMaster::onT35IntervalChanged()
{
    //    qDebug()<< Q_FUNC_INFO << "m_port->t35IntervalUsec() "<<m_port->t35IntervalUsec();
}


void ModbusRtuMaster::readDataFromModbusSlave(QString mSlaveAddr,QString mFunctionCode,QString mStartAddr,QString mNumOfRegister,QString mCommMode,QString mbyteCount)
{
    QByteArray slaveAddr = mSlaveAddr.simplified().mid(0, 2).toLatin1();
    QByteArray functionCode =mFunctionCode.simplified().mid(0, 2).toLatin1();
    QByteArray startAddr = mStartAddr.simplified().mid(0, 4).toLatin1();
    QByteArray writeData = "";
    QByteArray numOfRegister =QByteArray("0000" + mNumOfRegister.toLatin1()).right(4);
    QByteArray byteCount = "";
    QString commMode = mCommMode;
    byteCount.setNum(mbyteCount.toInt(0, 16) * 2, 16);
    byteCount = "00" + byteCount;
    byteCount = byteCount.right(2);

    QByteArray sendData = "";
    if( commMode.contains("RTU"))
    {
        int tempStartAddr =    startAddr.toInt(NULL, 16);
        startAddr = QByteArray("0000" + QByteArray::number(tempStartAddr, 16)).right(4);
        sendData = makeRTUFrame(slaveAddr, functionCode, startAddr, numOfRegister, byteCount, writeData);
    }
    else
        sendData = makeLSBUSFrame(slaveAddr, functionCode, startAddr, numOfRegister, byteCount, writeData);
    QStringList parsedData;
    onParseData(parsedData, sendData);//含CRC校验
    addDataToTxPktQueueModel(parsedData);
}

void ModbusRtuMaster::btnTxQueueFileOpenClicked()
{
    qDebug() << Q_FUNC_INFO;
}

void ModbusRtuMaster::btnTxqueueFileSaveClicked()
{
    qDebug() << Q_FUNC_INFO;

}

void ModbusRtuMaster::onModelTxPktQueueDataChanged(QModelIndex topLeft, QModelIndex bottomRight )
{
    Q_UNUSED(bottomRight);
    int row = topLeft.row();
    int col = topLeft.column();

    if( col == MODBUS_WND_TX_QUEUE_COLUMNS::HEX )
    {
        QString str = m_modelTxPktQueue->item(row, col)->text();
        QString asciiStr = "";
        str = str.trimmed();
        str = str.replace(" ", "");
        for(int i = 0; i < str.count()/2 ; i++ )
        {
            asciiStr += QByteArray::fromHex(str.mid(i*2, 2).toLatin1());
        }
        m_modelTxPktQueue->setItem(row, col+1, new QStandardItem(asciiStr));
    }
}

void ModbusRtuMaster::onTimerAutoSendTimeout()//定时发送
{
    onReadyEntered();
}

void ModbusRtuMaster::onModelTxResultRowInserted(const QModelIndex & parent, int first, int last)
{
    Q_UNUSED(parent);
    Q_UNUSED(first);
    Q_UNUSED(last);
    if( m_txRxResultMaxRow *2  < (unsigned int)m_modelTxRxResult->rowCount() )
    {
        QString saveFilePath = QDir::currentPath();
        QString filePath = saveFilePath + + "/" + QDateTime::currentDateTime().toString("MM-dd_HH-mm") + ".csv";
        QFile file;
        file.setFileName(filePath);
        QIODevice::OpenModeFlag openMode;
        openMode = QIODevice::WriteOnly;
        if( file.open(openMode) == true )
        {
            file.seek(file.size() );
            QString rowData = "";

            for(int i = 0 ; i < m_viewTxRxResult->horizontalHeader()->count() ; i ++ )
            {
                if( i  != m_viewTxRxResult->horizontalHeader()->count() - 1)
                    rowData += m_modelTxRxResult->horizontalHeaderItem(i)->text() + ",";
                else
                    rowData += m_modelTxRxResult->horizontalHeaderItem(i)->text() + "\n";
            }
            file.write(rowData.toLatin1());
            rowData = "";

            for(unsigned int i = 0 ; i < m_txRxResultMaxRow; i ++ )
            {
                QList <QStandardItem*> items;
                items = m_modelTxRxResult->takeRow(0);
                rowData = "";
                rowData += " " + items.at(MODBUS_WND_TXRX_RESULT_COLUMNS::TIMESTAMP)->text() + ",";
                rowData += items.at(MODBUS_WND_TXRX_RESULT_COLUMNS::STATUS)->text() + ",";
                rowData += items.at(MODBUS_WND_TXRX_RESULT_COLUMNS::MODE)->text() + ",";
                rowData += items.at(MODBUS_WND_TXRX_RESULT_COLUMNS::RAWDATA)->text() + ",";
                rowData += items.at(MODBUS_WND_TXRX_RESULT_COLUMNS::CRC)->text() + ",";
                rowData += items.at(MODBUS_WND_TXRX_RESULT_COLUMNS::SLAVE_ADDR)->text() + ",";
                rowData += items.at(MODBUS_WND_TXRX_RESULT_COLUMNS::FUNCTION)->text() + ",";
                rowData += items.at(MODBUS_WND_TXRX_RESULT_COLUMNS::INFO)->text() + "\n";

                file.write(rowData.toLatin1());

                foreach(QStandardItem* item, items)
                {
                    if( item != 0 )
                        delete item;
                }
            }
            file.flush();
        }
        else
        {
            qWarning() <<  Q_FUNC_INFO << "file save fail" << file.errorString();

        }
        file.close();
    }
}

void ModbusRtuMaster::sendModelTxPktQueue(int index )//向从设备发送数据
{
    if( index >= m_modelTxPktQueue->rowCount() )
    {
        qDebug() << Q_FUNC_INFO << "index overflow";
        return;
    }
    m_mutex.lock();
    QByteArray sendData;
    sendData = QByteArray::fromHex( m_modelTxPktQueue->item(index, MODBUS_WND_TX_QUEUE_COLUMNS::HEX )->text().toLatin1());
    //    qDebug() << Q_FUNC_INFO << sendData.toHex();
    QMetaObject::invokeMethod(m_port, "sendData", Qt::BlockingQueuedConnection,
                              Q_ARG(QByteArray, sendData ));
    m_mutex.unlock();
}

void ModbusRtuMaster::onReadyEntered()
{
    qDebug() << Q_FUNC_INFO;
    static int index = 0;
    if( m_modelTxPktQueue->rowCount() == 0)
    {
        return;
    }

    for(int count = 0; count < m_modelTxPktQueue->rowCount(); count ++)
    {
        int position = (index + count)  % m_modelTxPktQueue->rowCount();
        if( m_modelTxPktQueue->item(position, MODBUS_WND_TX_QUEUE_COLUMNS::ENABLE )->checkState() == Qt::Checked )
        {
            m_timerAutoSend->stop();
            sendModelTxPktQueue(position);
            index = (position+1) % m_modelTxPktQueue->rowCount();
            break;
        }
        else
        {
            m_timerAutoSend->start();
        }
    }
}

void ModbusRtuMaster::onReceiveWriteList(QStringList parsedData)
{
    m_mutexFotSerial.lock();
    QByteArray sendData;
    QString rawData = "";
    for(int i = 0; i < parsedData.count(); i++)
    {
        if(i==3)
        {
            rawData = parsedData.at(3);
        }
    }
    sendData = QByteArray::fromHex( rawData.toLatin1() );
    qDebug() << Q_FUNC_INFO << sendData.toHex();
    QMetaObject::invokeMethod(m_port, "sendData", Qt::BlockingQueuedConnection,
                              Q_ARG(QByteArray, sendData ));
    m_mutexFotSerial.unlock();
}

void ModbusRtuMaster::writeCmd(QString slaveAddr, QString functionCode,QString startAddr,QString numOfRegister,QString commMode,QString byteCount,QString inputDataString,bool intputFlag)
{
    QByteArray mSlaveAddr = slaveAddr.simplified().mid(0, 2).toLatin1();
    QByteArray mFunctionCode = functionCode.simplified().mid(0, 2).toLatin1();
    QByteArray mStartAddr = startAddr.simplified().mid(0, 4).toLatin1();
    QString writeData;
    QByteArray mWriteData;
    if(intputFlag){
        writeData = QString("%1").arg(inputDataString.toInt(), 4, 16, QLatin1Char('0'));//convert to hexString
        mWriteData = writeData.simplified().toLatin1();
    }
    else{
        mWriteData = inputDataString.simplified().toLatin1();
    }
    QByteArray mNumOfRegister = QByteArray("0000" + numOfRegister.toLatin1()).right(4);
    QByteArray mByteCount = "";
    mByteCount.setNum(byteCount.toInt(0, 16) , 16);
    mByteCount = "00" + mByteCount;
    mByteCount = mByteCount.right(2);
    QByteArray sendData = "";
    if( commMode.contains("RTU"))
    {
        int tempStartAddr =    mStartAddr.toInt(NULL, 16);
        mStartAddr = QByteArray("0000" + QByteArray::number(tempStartAddr, 16)).right(4);
        sendData = makeRTUFrame(mSlaveAddr, mFunctionCode, mStartAddr, mNumOfRegister, mByteCount, mWriteData);
    }
    else
        sendData = makeLSBUSFrame(mSlaveAddr, mFunctionCode, mStartAddr, mNumOfRegister, mByteCount, mWriteData);
    QStringList parsedData;
    onParseData(parsedData, sendData);//含CRC校验
    emit sendWriteList(parsedData);
}

void ModbusRtuMaster::toggleAutoSendFun(bool check)
{
    emit toggleAutoSend(check);
}

QByteArray ModbusRtuMaster::toHexString(QByteArray buf)
{
    QByteArray temp = "";
    for(int i =0; i < buf.count() ; i ++ )
    {
        if( i == buf.count() -1 )
            temp += buf.mid(i, 1).toHex();
        else
            temp += buf.mid(i, 1).toHex() + " ";
    }
    return temp;
}

bool ModbusRtuMaster::check_ModbusRTU_CRC16(QByteArray buf, quint16 &result)
{
    if( buf.size() >= 4 )
    {
        QByteArray srcCrc = buf.mid(buf.size()-2, 2);
        QByteArray dstCrc = "";
        quint16 crc = ModbusRtuMaster::ModbusRTU_CRC16(buf.constData(), buf.size() -2 );
        dstCrc = QByteArray::fromRawData((char*)&crc, 2);
        result = crc;
        if( srcCrc == dstCrc )
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    result = 0xffff;
    return false;
}

quint16 ModbusRtuMaster::ModbusRTU_CRC16 (const char *buf, quint16 wLength)
{
    static const quint16 wCRCTable[] = {
        0X0000, 0XC0C1, 0XC181, 0X0140, 0XC301, 0X03C0, 0X0280, 0XC241,
        0XC601, 0X06C0, 0X0780, 0XC741, 0X0500, 0XC5C1, 0XC481, 0X0440,
        0XCC01, 0X0CC0, 0X0D80, 0XCD41, 0X0F00, 0XCFC1, 0XCE81, 0X0E40,
        0X0A00, 0XCAC1, 0XCB81, 0X0B40, 0XC901, 0X09C0, 0X0880, 0XC841,
        0XD801, 0X18C0, 0X1980, 0XD941, 0X1B00, 0XDBC1, 0XDA81, 0X1A40,
        0X1E00, 0XDEC1, 0XDF81, 0X1F40, 0XDD01, 0X1DC0, 0X1C80, 0XDC41,
        0X1400, 0XD4C1, 0XD581, 0X1540, 0XD701, 0X17C0, 0X1680, 0XD641,
        0XD201, 0X12C0, 0X1380, 0XD341, 0X1100, 0XD1C1, 0XD081, 0X1040,
        0XF001, 0X30C0, 0X3180, 0XF141, 0X3300, 0XF3C1, 0XF281, 0X3240,
        0X3600, 0XF6C1, 0XF781, 0X3740, 0XF501, 0X35C0, 0X3480, 0XF441,
        0X3C00, 0XFCC1, 0XFD81, 0X3D40, 0XFF01, 0X3FC0, 0X3E80, 0XFE41,
        0XFA01, 0X3AC0, 0X3B80, 0XFB41, 0X3900, 0XF9C1, 0XF881, 0X3840,
        0X2800, 0XE8C1, 0XE981, 0X2940, 0XEB01, 0X2BC0, 0X2A80, 0XEA41,
        0XEE01, 0X2EC0, 0X2F80, 0XEF41, 0X2D00, 0XEDC1, 0XEC81, 0X2C40,
        0XE401, 0X24C0, 0X2580, 0XE541, 0X2700, 0XE7C1, 0XE681, 0X2640,
        0X2200, 0XE2C1, 0XE381, 0X2340, 0XE101, 0X21C0, 0X2080, 0XE041,
        0XA001, 0X60C0, 0X6180, 0XA141, 0X6300, 0XA3C1, 0XA281, 0X6240,
        0X6600, 0XA6C1, 0XA781, 0X6740, 0XA501, 0X65C0, 0X6480, 0XA441,
        0X6C00, 0XACC1, 0XAD81, 0X6D40, 0XAF01, 0X6FC0, 0X6E80, 0XAE41,
        0XAA01, 0X6AC0, 0X6B80, 0XAB41, 0X6900, 0XA9C1, 0XA881, 0X6840,
        0X7800, 0XB8C1, 0XB981, 0X7940, 0XBB01, 0X7BC0, 0X7A80, 0XBA41,
        0XBE01, 0X7EC0, 0X7F80, 0XBF41, 0X7D00, 0XBDC1, 0XBC81, 0X7C40,
        0XB401, 0X74C0, 0X7580, 0XB541, 0X7700, 0XB7C1, 0XB681, 0X7640,
        0X7200, 0XB2C1, 0XB381, 0X7340, 0XB101, 0X71C0, 0X7080, 0XB041,
        0X5000, 0X90C1, 0X9181, 0X5140, 0X9301, 0X53C0, 0X5280, 0X9241,
        0X9601, 0X56C0, 0X5780, 0X9741, 0X5500, 0X95C1, 0X9481, 0X5440,
        0X9C01, 0X5CC0, 0X5D80, 0X9D41, 0X5F00, 0X9FC1, 0X9E81, 0X5E40,
        0X5A00, 0X9AC1, 0X9B81, 0X5B40, 0X9901, 0X59C0, 0X5880, 0X9841,
        0X8801, 0X48C0, 0X4980, 0X8941, 0X4B00, 0X8BC1, 0X8A81, 0X4A40,
        0X4E00, 0X8EC1, 0X8F81, 0X4F40, 0X8D01, 0X4DC0, 0X4C80, 0X8C41,
        0X4400, 0X84C1, 0X8581, 0X4540, 0X8701, 0X47C0, 0X4680, 0X8641,
        0X8201, 0X42C0, 0X4380, 0X8341, 0X4100, 0X81C1, 0X8081, 0X4040 };

    quint8 nTemp;
    quint16 CRC16 = 0xFFFF;

    while (wLength--)
    {
        nTemp = *buf++ ^ CRC16;
        CRC16 >>= 8;
        CRC16  ^= wCRCTable[nTemp];
    }
    return CRC16;
} // End: CRC16


quint8 ModbusRtuMaster::LSBUS_sum (QByteArray buf)
{
    quint8 sum = 0x00;
    for ( int i = 1 ; i < buf.count() ; i ++ )
    {
        //        qDebug() << QByteArray(1, buf.at(i)).toHex();
        sum += buf.at(i);
    }
    //     qDebug() << "sum" << QByteArray(1, sum).toHex();
    return sum;
}

QByteArray ModbusRtuMaster::makeRTUFrame(QByteArray slaveAddr, QByteArray functionCode, QByteArray startAddr,
                                         QByteArray numOfRegister, QByteArray byteCount, const QByteArray writeData)
{
    Q_ASSERT(writeData.size() <= 252);
    QByteArray modbusPDU = "";



    switch( QByteArray::fromHex(functionCode).at(0) )
    {
    case 0x02:
        modbusPDU += functionCode + startAddr + numOfRegister;//增加读报警信息
        break;
    case 0x03:
        modbusPDU += functionCode + startAddr + numOfRegister;
        break;
    case 0x04:
        modbusPDU += functionCode + startAddr + numOfRegister;
        break;
    case 0x05:
        modbusPDU += functionCode + startAddr + writeData;
        break;
    case 0x06:
        modbusPDU += functionCode + startAddr + writeData;
        break;
    case 0x10:
        modbusPDU += functionCode + startAddr + numOfRegister + byteCount + writeData;
        break;
    default:
        modbusPDU += functionCode + startAddr + writeData;
        break;
    }
    QByteArray frame ="";
    QDataStream ds(&frame, QIODevice::WriteOnly);
    ds.setByteOrder(QDataStream::LittleEndian);
    ds << quint8(QByteArray::fromHex(slaveAddr).at(0));
    ds.writeRawData(QByteArray::fromHex(modbusPDU).constData(), modbusPDU.size() /2 );
    quint16 crc = ModbusRTU_CRC16(frame.constData(), frame.size());
    ds << quint16(crc);
    return frame;
}

QByteArray ModbusRtuMaster::makeLSBUSFrame(QByteArray slaveAddr, QByteArray functionCode, QByteArray startAddr,
                                           QByteArray numOfRegister, QByteArray byteCount, const QByteArray writeData)
{
    Q_ASSERT(writeData.size() <= 252);
    QByteArray modbusPDU = "";

    Q_UNUSED(byteCount);
    switch( QByteArray::fromHex(functionCode).at(0) )
    {
    case 0x52:
        modbusPDU += slaveAddr + QByteArray::fromHex(functionCode).at(0) + startAddr + numOfRegister.right(1);
        break;
    case 0x57:
        modbusPDU += slaveAddr + QByteArray::fromHex(functionCode).at(0) + startAddr + numOfRegister.right(1) + writeData;
        break;
    case 0x58:
        break;
    case 0x59:
        break;
    default:
        break;
    }
    QByteArray frame ="";
    QDataStream ds(&frame, QIODevice::WriteOnly);
    ds.setByteOrder(QDataStream::LittleEndian);
    ds << quint8(0x05);
    ds.writeRawData(modbusPDU.constData(), modbusPDU.size());
    quint8 sum = LSBUS_sum(frame);
    ds.writeRawData( QByteArray::number(sum, 16).toUpper().constData(), 2 );
    ds << quint8(0x04);
    return frame;
}






